package com.orkasgb.framework.springsecurity;

import io.jsonwebtoken.*;
import io.micrometer.common.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;

/**
 * 自定义JWT生成器，用于生成JWT和验证JWT。
 *
 * @date
 * @since
 */
@Component
public class OrkasgbJWTSecurityContextRepository implements SecurityContextRepository {

    private static final SecretKey ORKASGB_AUTHENTICATINO_JWT_KEY =
            new SecretKeySpec("ORKASGB_AUTHENTICATINO_JWT".getBytes(StandardCharsets.UTF_8),  "AES");;
    
    private static final String ORKASGB_AUTHENTICATINO_TOKEN_KEY = "ORKASGB:AUTHENTICATION:";

    private static final JwtParser JWT_PARSER = Jwts.parser().setSigningKey(ORKASGB_AUTHENTICATINO_JWT_KEY);

    private static final long ORKASGB_AUTHENTICATION_EXPIRE_IN_MS = Duration.ofMinutes(2).toMillis();

    private static final Map<String, Object> JWT_GRANTED_AUTHORITY_MAP = new HashMap<>(256);

    @Override
    public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
        HttpServletRequest request = requestResponseHolder.getRequest();
        String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);

        SecurityContext context = SecurityContextHolder.createEmptyContext();
        if(StringUtils.isBlank(authorization)){
            return context;
        }

        Jws<Claims> claimsJws;
        try {
            // 解析JWT
            claimsJws = JWT_PARSER.parseClaimsJws(authorization);
        }catch (Exception e) {
            return context;
        }
        Claims claims = claimsJws.getBody();
        String subject = claims.getSubject();
        UserDetails userDetails = (UserDetails) JWT_GRANTED_AUTHORITY_MAP.get("authorities" + subject);
        Collection<GrantedAuthority> grantedAuthorityList = new ArrayList<>(userDetails.getAuthorities());
        OrkasgbJWTSecurityContextToken orkasgbJWTSecurityContextToken =
                new OrkasgbJWTSecurityContextToken(grantedAuthorityList, grantedAuthorityList, subject);
        context.setAuthentication(orkasgbJWTSecurityContextToken);

        return context;
    }

    @Override
    public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
        UserDetails userDetails = (UserDetails) context.getAuthentication().getPrincipal();
        JWT_GRANTED_AUTHORITY_MAP.put("authorities" + userDetails.getUsername(), userDetails);
        String token = Jwts.builder()
                .setId(userDetails.getUsername().concat("_").concat(UUID.randomUUID().toString()))
                .setSubject(userDetails.getUsername()) // 发行者
                .setIssuer(userDetails.getUsername()) // 发行者
                .setIssuedAt(Calendar.getInstance().getTime()) // 发行时间
                .signWith(SignatureAlgorithm.HS256, ORKASGB_AUTHENTICATINO_JWT_KEY) // 签名类型 与 密钥
                .compressWith(CompressionCodecs.DEFLATE) // 对载荷进行压缩
                .setExpiration(new Date(System.currentTimeMillis() + ORKASGB_AUTHENTICATION_EXPIRE_IN_MS))
                .compact();

        response.setHeader(ORKASGB_AUTHENTICATINO_TOKEN_KEY, token);
        request.setAttribute("accessToken", token);
    }

    @Override
    public boolean containsContext(HttpServletRequest request) {
        String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
        if(StringUtils.isBlank(authorization)) {
            return false;
        }

        try {
            JWT_PARSER.parseClaimsJws(authorization);
        }catch (Exception e) {
            return false;
        }

        return true;
    }
}
